/*--
 * MainWindow.cs: Implements the main Registrar window.
 *--
 * For more information, please visit the Registrar Project site:
 *   http://code.google.com/p/registrar/
 *--
 * (c) 2006 Christopher E. Granade.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; exactly version 2
 * of the License applies to this software.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

using System;
using System.Collections.Generic;
using Microsoft.Win32;
using Gtk;

namespace Registrar {

	public class MainWindow: Gtk.Window {

		#region Private Member Variables

			private const int
				KeyNameCol = 0,
				KeyKeyCol = 1,
				ValueNameCol = 0,
				ValueDataCol = 1,
				FormatR4 = 0,
				FormatRegX = 1,
				NumFormats = 2;

			private RegistryKey currentkey = null;

			protected Gtk.TreeView keystree;
			protected Gtk.TreeView valuestree;

			private Gdk.Pixbuf
				hiveicon = 
					Gtk.IconTheme.Default.LoadIcon("stock_book_blue", 16, 0),
				foldericon =
					Gtk.IconTheme.Default.LoadIcon("gtk-directory", 16, 0),
				nulltypeicon =
					Gtk.IconTheme.Default.LoadIcon("stock_placeholder-text", 16, 0);
			
			private Dictionary<RegistryKey, string>
				hivenames = new Dictionary<RegistryKey, string>();
				
			private Dictionary<Type, Gdk.Pixbuf>
				typeicons = new Dictionary<Type, Gdk.Pixbuf>();

			protected Gtk.TreeStore
				keysstore =
					new Gtk.TreeStore(
					    typeof(string),
					    typeof(RegistryKey));
			
			protected Gtk.ListStore
				valuestore =
					new Gtk.ListStore(typeof(string), typeof(object));
			
			protected Gtk.Action DeleteKey;
			
			private List<IRegistrySerializer> serializers
				= new List<IRegistrySerializer>(NumFormats);
				
			private List<FileFilter> filefilters
				= new List<FileFilter>(NumFormats);
		
		#endregion
		
		private void InitSerializers() {
			
			serializers.Add(new TextSerializer());
			serializers.Add(new XmlSerializer());
			
		}
		
		private void InitTypeIcons() {
		
			typeicons.Add(
				typeof(string),
				Gtk.IconTheme.Default.LoadIcon("stock_styles-character-styles", 16, 0));
				
			typeicons.Add(
				typeof(int),
				Gtk.IconTheme.Default.LoadIcon("stock_page-number", 16, 0));
				
		}
		
		private void RenderValIcon(
			Gtk.TreeViewColumn col, Gtk.CellRenderer cell,
			Gtk.TreeModel model, Gtk.TreeIter iter
		) {
		
			object o = model.GetValue(iter, ValueDataCol);
		
			try {
				(cell as CellRendererPixbuf).Pixbuf =
					typeicons[o.GetType()];
			} catch (KeyNotFoundException knfe) {
				(cell as CellRendererPixbuf).Pixbuf =
					nulltypeicon;
			} catch (NullReferenceException nre) {
				(cell as CellRendererPixbuf).Pixbuf =
					nulltypeicon;
			}
			
		}
		
		private void RenderValueData(
			Gtk.TreeViewColumn col, Gtk.CellRenderer cell,
			Gtk.TreeModel model, Gtk.TreeIter iter
		) {
			
			object o = model.GetValue(iter, ValueDataCol);
			
			try {
				(cell as CellRendererText).Text =
					o.ToString();
			} catch (Exception e) {
				(cell as CellRendererText).Text = "";
			}
			
		}
		
		private void RenderKeyIcon(
			Gtk.TreeViewColumn col, Gtk.CellRenderer cell,
			Gtk.TreeModel model, Gtk.TreeIter iter
		) {
		
			if (model.GetPath(iter).Depth > 1) {
				(cell as CellRendererPixbuf).Pixbuf = foldericon;
				(cell as CellRendererPixbuf).PixbufExpanderClosed = foldericon;
				(cell as CellRendererPixbuf).PixbufExpanderOpen = foldericon;
			} else {
				(cell as CellRendererPixbuf).Pixbuf = hiveicon;
				(cell as CellRendererPixbuf).PixbufExpanderClosed = hiveicon;
				(cell as CellRendererPixbuf).PixbufExpanderOpen = hiveicon;
			}
			
		}
		
		private void InitTrees() {
		
			Gtk.TreeViewColumn
				keynamecol = new TreeViewColumn(),
				valuenamecol = new TreeViewColumn(),
				valuecol = new TreeViewColumn();
		
			valuenamecol.Title = "Value Name";
			valuecol.Title = "Value Data";
		
			CellRendererPixbuf crp = new CellRendererPixbuf();
			keynamecol.Title = "Key Name";
			keynamecol.PackStart(crp, false);
			keynamecol.SetCellDataFunc(crp, new TreeCellDataFunc(RenderKeyIcon));
			
			CellRendererText crt = new CellRendererText();
			keynamecol.PackStart(crt, false);
			keynamecol.AddAttribute(crt, "text", 0);
			crt.Editable = true;
			crt.Edited += OnKeyNameCellEdited;
				
			CellRendererPixbuf vicell = new CellRendererPixbuf();
			valuenamecol.PackStart(vicell, false);
			valuenamecol.SetCellDataFunc(vicell, new TreeCellDataFunc(RenderValIcon));
			
			CellRendererText vncell = new CellRendererText();
			vncell.Editable = true;
			vncell.Edited += OnValNameCellEdited;
			valuenamecol.PackStart(vncell, false);
			valuenamecol.AddAttribute(vncell, "text", ValueNameCol);
			
			
			valuestree.AppendColumn(valuenamecol);
		
			CellRendererText datacell = new CellRendererText();
			datacell.Editable = true;
			datacell.Edited += OnDataCellEdited;
			valuecol.PackStart(datacell, false);
			valuecol.SetCellDataFunc(datacell, new TreeCellDataFunc(RenderValueData));
				
			valuestree.AppendColumn(valuecol);
				
			keystree.AppendColumn(keynamecol);
			
			keystree.Model = keysstore;
			keystree.HeadersVisible = false;	
					
			valuestree.Model = valuestore;
			valuestree.HeadersVisible = true;
		
		}
		
		private void PopulateValues(RegistryKey rk) {
			
			foreach (string valname in rk.GetValueNames()) {
				// Get the value.
				object o = rk.GetValue(valname);
				
				valuestore.AppendValues(valname, o);
			}
			
		}
		
		private void PopulateKeys() {
			
			// Populate each key in turn.
			Gtk.TreeIter iter;
			
			foreach (KeyValuePair<RegistryKey, string> kvp in hivenames) {
				iter = keysstore.AppendValues(kvp.Value, kvp.Key);
				PopulateKeys(iter, kvp.Key);
			}
			
		}
		
		private void PopulateHiveNames() {
		
			hivenames.Add(Registry.ClassesRoot, "Classes Root");
			hivenames.Add(Registry.CurrentUser, "Current User");
			hivenames.Add(Registry.LocalMachine, "Local Machine");
			hivenames.Add(Registry.Users, "Users");
			hivenames.Add(Registry.CurrentConfig, "Current Configuration");
			hivenames.Add(Registry.DynData, "Dynamic Data");
			hivenames.Add(Registry.PerformanceData, "Performance Data");
		
		}
		
		private void PopulateKeys(Gtk.TreeIter iter, RegistryKey rk, bool repopulate) {
			
			if (keysstore.IterHasChild(iter)) {
				// Already children there... let's either clear them or return.
				if (!repopulate) {
					return;
				}
			}
		
			string[] sknames = null;
			
			try {
				sknames = rk.GetSubKeyNames();
				foreach (string skname in sknames) {
					try {
						RegistryKey sk = rk.CreateSubKey(skname);
						TreeIter subiter = keysstore.AppendValues(iter, skname, sk);
						//PopulateKeys(subiter, sk);
					} catch (Exception e) {}
				}
			} catch (System.IO.IOException ioe) {
			} catch (System.UnauthorizedAccessException uae) {}
			
			
			
			
		}
		
		private void PopulateKeys(Gtk.TreeIter iter, RegistryKey rk) {
		
			PopulateKeys(iter, rk, true);
			
		}
		
		public MainWindow (): base ("") {
		
			// Call Stetic to make the GUI.
			Stetic.Gui.Build(this, typeof(MainWindow));
			
			// Initialize various private member variables.
			InitTypeIcons();
			InitSerializers();
			InitFilters();
			PopulateHiveNames();
			
			// Initialize the TreeViews.
			InitTrees();
			
			// Add the first level of keys.
			PopulateKeys();
			
		}
		
		protected void OnDeleteEvent (object sender, DeleteEventArgs a) {
			Application.Quit ();
			a.RetVal = true;
		}
		
		protected void OnDataCellEdited(object o, Gtk.EditedArgs args) {
			
			Gtk.TreeIter iter;
			valuestore.GetIter(out iter, new TreePath(args.Path));
			
			valuestore.SetValue(iter, ValueDataCol, args.NewText);
			
			string
				valuename =
					(string)valuestore.GetValue(iter, ValueNameCol),
				valuedata = args.NewText;
			
			
			currentkey.SetValue(valuename, valuedata);
			
		}
		
		
		protected virtual void OnKeystreeRowExpanded(object o, Gtk.RowExpandedArgs args) {
			
			// Get the rk from the data model, and expand.
			Gtk.TreeIter childiter;
			bool morekids = keysstore.IterChildren(out childiter, args.Iter);
			
			while (morekids) {
				PopulateKeys(
					childiter,
					(RegistryKey)keysstore.GetValue(childiter, KeyKeyCol),
					false);
				morekids = keysstore.IterNext(ref childiter);
			}
			
		}

		private void RemoveTreeItem(TreeModel m, TreePath p, TreeIter i) {
		
			// Get the parent.
			TreeIter pi;
			m.IterParent(out pi, i);
			
			// Get the registry keys.
			RegistryKey crk = (RegistryKey)(m.GetValue(i,  KeyKeyCol)),
			            prk = (RegistryKey)(m.GetValue(pi, KeyKeyCol));
			
			// Get the subkey name.
			string skn = crk.Name.Substring(crk.Name.LastIndexOf('\\')+1);
			
			// Drop the key.
			prk.DeleteSubKeyTree(skn);
			
			// Remove the spot on the tree.
			(m as TreeStore).Remove(ref i);
			
		}

		protected virtual void OnDeleteKeyActivated(object sender, System.EventArgs e) {
		
			// Delete the activated keys.
			keystree.Selection.SelectedForeach(
				new Gtk.TreeSelectionForeachFunc(RemoveTreeItem));
		
		}

		protected virtual void OnAddKeyActivated(object sender, System.EventArgs e)	{
			
			TreeIter iter = GetSingleKeySel();
			RegistryKey rk = GetKeyFromIter(iter);
			
			// Find a good name.
			int i = 1; string s = "New Key";
			RegistryKey n = rk.OpenSubKey(s);
			if (n != null) {
				s = s + " ";
				while (n != null)
					n = rk.OpenSubKey(s + i.ToString());
				s = s + i.ToString();
			}
			
			// Create the key.
			n = rk.CreateSubKey(s);
			
			keysstore.AppendValues(iter, s, n);
			
		}

		private RegistryKey GetKeyFromIter(Gtk.TreeIter i) {
		
			return (RegistryKey)(keysstore.GetValue(i, KeyKeyCol));
			
		}

		private void ShowMessage(string s) {
			MessageDialog md = new MessageDialog(
				this, DialogFlags.DestroyWithParent,
				MessageType.Warning, ButtonsType.Ok, s);
			md.Modal = false;
			md.Run();
			md.Destroy();
		}

		protected void OnKeyNameCellEdited(object o, Gtk.EditedArgs args) {
			
			Gtk.TreeIter iter, piter;
			keysstore.GetIter(out iter, new TreePath(args.Path));
			keysstore.IterParent(out piter, iter);
			
			string oldname = (string)keysstore.GetValue(iter, KeyNameCol),
			       newname = args.NewText;
			
			RegistryKey rk = GetKeyFromIter(piter);
			
			try {
				RegistryUtils.MoveKey(rk, oldname, newname);
				keysstore.SetValue(iter, KeyKeyCol, rk.CreateSubKey(newname));
			} catch (Exception e) {
				System.Console.WriteLine(e);
			
				return;
			}
			
			keysstore.SetValue(iter, KeyNameCol, newname);
			
		}
		
		protected void OnValNameCellEdited(object o, Gtk.EditedArgs args) {
			
			Gtk.TreeIter iter;
			valuestore.GetIter(out iter, new TreePath(args.Path));
			
			string
				valuename = args.NewText,
				valuedata = (string)valuestore.GetValue(iter, ValueDataCol),
				oldname = (string)valuestore.GetValue(iter, ValueNameCol);
				
			currentkey.SetValue(valuename, valuedata);
			valuestore.SetValue(iter, ValueNameCol, valuename);
			
			try {
				currentkey.DeleteValue(oldname);
			} catch (ArgumentException ae) {
				return;
			}
			
		} 

		protected virtual void OnAddValueActivated(object sender, System.EventArgs e) {
			
			if (currentkey == null) return;
			
			string s = "New Value";
			int i = 1;
			
			if (currentkey.GetValue("New Value") != null) {
				while (currentkey.GetValue(s + " " + i.ToString()) != null) i++;
				s = s + " " + i.ToString();
			}
			
			currentkey.SetValue(s, "");
			valuestore.AppendValues(s, "");
			
		}

		protected virtual void OnDeleteValueActivated(object sender, System.EventArgs e) {
			
			// TODO! Delete values.
			
		}

		protected virtual void OnQuitActivated(object sender, System.EventArgs e) {
			Application.Quit();
		}

		private TreeIter GetSingleKeySel() {
		
			TreeIter i;
			keystree.Selection.GetSelected(out i);
			return i;
			
		}

		protected virtual void OnKeystreeCursorChanged(object sender, System.EventArgs e) {
			TreeIter iter = GetSingleKeySel();
			
			currentkey =
				(RegistryKey)keystree.Model.GetValue(iter, KeyKeyCol);
				
			valuestore.Clear();
			
			PopulateValues(currentkey);
		}

		private void AddFiltersToDialog(FileChooserDialog fcd) {
			
			foreach (FileFilter ff in filefilters) {
				fcd.AddFilter(ff);
			}
			
		}

		private void InitFilters() {
		
			while (filefilters.Count < NumFormats) {
				filefilters.Add(new FileFilter());
			}
		
			filefilters[FormatR4].AddPattern("*.reg");
			filefilters[FormatR4].Name = "REGEDIT 4 Formatted File (*.reg)";
			filefilters[FormatR4].UserData = (IntPtr)FormatR4;
			
			filefilters[FormatRegX].AddPattern("*.regx");
			filefilters[FormatRegX].Name = "XML Registry Format (*.regx)";
			filefilters[FormatRegX].UserData = (IntPtr)FormatRegX;
		
		}

		protected virtual void OnExportActivated(object sender, System.EventArgs e) {
		
			// Where to?
			FileChooserDialog fcd = new FileChooserDialog(
				"Export Registry Key", this,
				FileChooserAction.Save);
			fcd.AddButton(Stock.Cancel, ResponseType.Cancel);
			fcd.AddButton(Stock.Ok, ResponseType.Ok);
			fcd.Modal = true;
			fcd.SelectMultiple = false;
			
			AddFiltersToDialog(fcd);
			
			// OK, we built the FileChooserDialog, so run it.
			if (fcd.Run() == (int)ResponseType.Cancel) {
				fcd.Destroy();
				return;
			}
			
			string fname = fcd.Filename;
			
			serializers[(int)fcd.Filter.UserData].Export(
				currentkey, fname);
				
			fcd.Destroy();
			
		}
		
		
	}
	
}
